/*  FSM template example implementation

    Copyright (C) 2013 Dushara Jayasinghe.

    fsm_template is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    fsm_template is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with fsm_template.  If not, see <http://www.gnu.org/licenses/>.
*/

/*
 * This is the implementation of the calculator state machine described
 * by Miro Samek using the fsm_template. It implements all the state
 * transitions.
 * 
 * It doesn't implement the calculator functionality (e.g. storing
 * entered values and performing operations). It merely transitions
 * through the states when the appropriate events are generated when the
 * interface functions are invoked by the caller.
 */
#include <stdio.h>
#include <calc.h>

/* Select the switch template implementation */
#define FSM_TEMPLATE_TYPE   FSM_SWITCHED_TEMPLATE

/* Define a list of the states */
#define STATE_LIST(DEFINE_STATE)    \
    DEFINE_STATE(result)            \
    DEFINE_STATE(begin)             \
    DEFINE_STATE(negated1)          \
    DEFINE_STATE(zero1)             \
    DEFINE_STATE(int1)              \
    DEFINE_STATE(frac1)             \
    DEFINE_STATE(opEntered)         \
    DEFINE_STATE(negated2)          \
    DEFINE_STATE(zero2)             \
    DEFINE_STATE(int2)              \
    DEFINE_STATE(frac2)             \
    DEFINE_STATE(error)

/* Specify the initial state (must be postfixed with _state */
#define INITIAL_STATE begin_state

/* Define a list of all the events */
#define EVENT_LIST(DEFINE_EVENT, __fsm_internal_arg__)   \
    DEFINE_EVENT(C,             { int __dummy__; }, __fsm_internal_arg__ ) \
    DEFINE_EVENT(CE,            { int __dummy__; }, __fsm_internal_arg__ ) \
    DEFINE_EVENT(subtract_op,   { int __dummy__; }, __fsm_internal_arg__ ) \
    DEFINE_EVENT(add_op,        { int __dummy__; }, __fsm_internal_arg__ ) \
    DEFINE_EVENT(multiply_op,   { int __dummy__; }, __fsm_internal_arg__ ) \
    DEFINE_EVENT(divide_op,     { int __dummy__; }, __fsm_internal_arg__ ) \
    DEFINE_EVENT(EQUALS,        { int __dummy__; }, __fsm_internal_arg__ ) \
    DEFINE_EVENT(DIGIT_0,       { int __dummy__; }, __fsm_internal_arg__ ) \
    DEFINE_EVENT(DIGIT_1_9,     { uint8_t digit; }, __fsm_internal_arg__ ) \
    DEFINE_EVENT(POINT,         { int __dummy__; }, __fsm_internal_arg__ )

/* Define the return type for the event actions */
#define RETURN_TYPE int

/*
 * The STATE_LIST and EVENT_LIST macros generate essentially a 2D
 * array of state event actions of the form:
 * 
 *      <state_name>_<event_name>_action(const <event_name_t>* payload, state_t* state);
 * 
 * We combine common event handlers into super states by using the
 * preprocessor to override/merge the common action functions.
 * 
 * The following delegates handling the 'C' key to to the on state
 */
 
/* on super state */
#define result_C_action     on_C_action
#define begin_C_action      on_C_action
#define negated1_C_action   on_C_action
#define zero1_C_action      on_C_action
#define int1_C_action       on_C_action
#define frac1_C_action      on_C_action
#define error_C_action      on_C_action
#define opEntered_C_action  on_C_action
#define negated2_C_action   on_C_action
#define zero2_C_action      on_C_action
#define int2_C_action       on_C_action
#define frac2_C_action      on_C_action

/* ready state */
#define begin_DIGIT_0_action        ready_DIGIT_0_action
#define result_DIGIT_0_action       ready_DIGIT_0_action
#define begin_DIGIT_1_9_action      ready_DIGIT_1_9_action
#define result_DIGIT_1_9_action     ready_DIGIT_1_9_action
#define begin_POINT_action          ready_POINT_action
#define result_POINT_action         ready_POINT_action
#define result_add_op_action        ready_add_op_action
#define begin_add_op_action         ready_add_op_action
#define begin_divide_op_action      ready_divide_op_action
#define result_divide_op_action     ready_divide_op_action
#define begin_multiply_op_action    ready_multiply_op_action
#define result_multiply_op_action   ready_multiply_op_action

/* operand1 state */
#define zero1_subtract_op_action    operand1_subtract_op_action
#define int1_subtract_op_action     operand1_subtract_op_action
#define frac1_subtract_op_action    operand1_subtract_op_action
#define zero1_add_op_action         operand1_add_op_action
#define int1_add_op_action          operand1_add_op_action
#define frac1_add_op_action         operand1_add_op_action
#define zero1_multiply_op_action    operand1_multiply_op_action
#define int1_multiply_op_action     operand1_multiply_op_action
#define frac1_multiply_op_action    operand1_multiply_op_action
#define zero1_divide_op_action      operand1_divide_op_action
#define int1_divide_op_action       operand1_divide_op_action
#define frac1_divide_op_action      operand1_divide_op_action
#define zero1_CE_action             operand1_CE_action
#define int1_CE_action              operand1_CE_action
#define frac1_CE_action             operand1_CE_action

/* operand2 state */
#define zero2_CE_action             operand2_CE_action
#define int2_CE_action              operand2_CE_action
#define frac2_CE_action             operand2_CE_action
#define zero2_divide_op_action      operand2_divide_op_action
#define int2_divide_op_action       operand2_divide_op_action
#define frac2_divide_op_action      operand2_divide_op_action
#define zero2_add_op_action         operand2_add_op_action
#define int2_add_op_action          operand2_add_op_action
#define frac2_add_op_action         operand2_add_op_action
#define zero2_subtract_op_action    operand2_subtract_op_action
#define int2_subtract_op_action     operand2_subtract_op_action
#define frac2_subtract_op_action    operand2_subtract_op_action
#define zero2_multiply_op_action    operand2_multiply_op_action
#define int2_multiply_op_action     operand2_multiply_op_action
#define frac2_multiply_op_action    operand2_multiply_op_action
#define zero2_EQUALS_action         operand2_EQUALS_action
#define int2_EQUALS_action          operand2_EQUALS_action
#define frac2_EQUALS_action         operand2_EQUALS_action

/* handlers for ignored events */
#define begin_CE_action                 ignore_CE_action
#define result_CE_action                ignore_CE_action
#define opEntered_CE_action             ignore_CE_action
#define error_CE_action                 ignore_CE_action
#define zero1_DIGIT_0_action            ignore_DIGIT_0_action
#define error_DIGIT_0_action            ignore_DIGIT_0_action
#define error_DIGIT_1_9_action          ignore_DIGIT_1_9_action
#define begin_EQUALS_action             ignore_EQUALS_action
#define result_EQUALS_action            ignore_EQUALS_action
#define error_subtract_op_action        ignore_subtract_op_action
#define negated1_add_op_action          ignore_add_op_action
#define opEntered_add_op_action         ignore_add_op_action
#define negated2_add_op_action          ignore_add_op_action
#define error_add_op_action             ignore_add_op_action
#define negated1_multiply_op_action     ignore_multiply_op_action
#define opEntered_multiply_op_action    ignore_multiply_op_action
#define negated2_multiply_op_action     ignore_multiply_op_action
#define error_multiply_op_action        ignore_multiply_op_action
#define negated1_divide_op_action       ignore_divide_op_action
#define opEntered_divide_op_action      ignore_divide_op_action
#define negated2_divide_op_action       ignore_divide_op_action
#define error_divide_op_action          ignore_divide_op_action
#define negated1_EQUALS_action          ignore_EQUALS_action
#define opEntered_EQUALS_action         ignore_EQUALS_action
#define negated2_EQUALS_action          ignore_EQUALS_action
#define zero1_EQUALS_action             ignore_EQUALS_action
#define int1_EQUALS_action              ignore_EQUALS_action
#define frac1_EQUALS_action             ignore_EQUALS_action
#define error_EQUALS_action             ignore_EQUALS_action
#define error_POINT_action              ignore_POINT_action

/* Include the state machine template to make all the magic happen 
 * 
 * See the comments in fsm_template.h. It documents the template
 * extensively.
 * 
 * */
#include <../fsm_template.h>

/* on state actions */
static int on_C_action(const C_t* payload, state_id_t* state)
{
    printf("on_C_action\n");
    *state = begin_state;
    
    return 0;
}

/* ready state actions */
static int ready_DIGIT_0_action(const DIGIT_0_t* payload, state_id_t* state)
{
    printf("ready:DIGIT_0\n");
    *state = zero1_state;
    
    return 0;
}

static int ready_DIGIT_1_9_action(const DIGIT_1_9_t* payload, state_id_t* state)
{
    printf("ready:DIGIT_1_9\n");
    *state = int1_state;
    
    return 0;
}

static int ready_POINT_action(const POINT_t* payload, state_id_t* state)
{
    printf("ready:POINT\n");
    *state = frac1_state;
    
    return 0;
}

static int ready_add_op_action(const add_op_t* payload, state_id_t* state)
{
    printf("ready:add_op\n");
    *state = opEntered_state;
    
    return 0;
}

static int ready_divide_op_action(const divide_op_t* payload, state_id_t* state)
{
    printf("ready:divide_op\n");
    *state = opEntered_state;
    
    return 0;
}

static int result_multiply_op_action(const multiply_op_t* payload, state_id_t* state)
{
    printf("ready:multiply_op\n");
    *state = opEntered_state;
    
    return 0;
}

/* result state actions */
static int result_subtract_op_action(const subtract_op_t* payload, state_id_t* state)
{
    printf("result:subtract_op\n");
    *state = opEntered_state;
    
    return 0;
}

/* begin state actions */
static int begin_subtract_op_action(const subtract_op_t* payload, state_id_t* state)
{
    printf("begin:subtract_op\n");
    *state = negated1_state;
    return 0;
}

/* operand1 state */
static int operand1_subtract_op_action(const subtract_op_t* payload, state_id_t* state)
{
    printf("operand1:subtract_op\n");
    *state = opEntered_state;
    
    return 0;
}

static int operand1_add_op_action(const add_op_t* payload, state_id_t* state)
{
    printf("operand1:add_op\n");
    *state = opEntered_state;
    
    return 0;
}

static int operand1_multiply_op_action(const multiply_op_t* payload, state_id_t* state)
{
    printf("operand1:multiply_op\n");
    *state = opEntered_state;

    return 0;
}

static int operand1_divide_op_action(const divide_op_t* payload, state_id_t* state)
{
    printf("operand1:divide_op\n");
    *state = opEntered_state;

    return 0;
}

static int operand1_CE_action(const CE_t* payload, state_id_t* state)
{
    printf("operand1:CE\n");
    *state = begin_state;
    
    return 0;
}

/* negated1 state */
static int negated1_subtract_op_action(const subtract_op_t* payload, state_id_t* state)
{
    printf("negated1:subtract_op\n");
    
    return 0;
}

static int negated1_CE_action(const CE_t* payload, state_id_t* state)
{
    printf("negated1:CE\n");
    *state = begin_state;
    
    return 0;
}

static int negated1_DIGIT_0_action(const DIGIT_0_t* payload, state_id_t* state)
{
    printf("negated1:DIGIT_0\n");
    *state = zero1_state;
    
    return 0;
}

static int negated1_DIGIT_1_9_action(const DIGIT_1_9_t* payload, state_id_t* state)
{
    printf("negated1:DIGIT_1_9\n");
    *state = int1_state;
    
    return 0;
}

static int negated1_POINT_action(const POINT_t* payload, state_id_t* state)
{
    printf("negated1:POINT\n");
    *state = frac1_state;
    
    return 0;
}

/* zero1 state */
static int zero1_DIGIT_1_9_action(const DIGIT_1_9_t* payload, state_id_t* state)
{
    printf("zero1:DIGIT_1_9\n");
    *state = int1_state;
    
    return 0;
}

static int zero1_POINT_action(const POINT_t* payload, state_id_t* state)
{
    printf("zero1:POINT\n");
    *state = frac1_state;
    
    return 0;
}

/* int1 state */
static int int1_DIGIT_0_action(const DIGIT_0_t* payload, state_id_t* state)
{
    return 0;
}

static int int1_DIGIT_1_9_action(const DIGIT_1_9_t* payload, state_id_t* state)
{
    return 0;
}

static int int1_POINT_action(const POINT_t* payload, state_id_t* state)
{
    printf("int1:POINT\n");
    *state = frac1_state;
    
    return 0;
}

/* frac1 state */
static int frac1_DIGIT_0_action(const DIGIT_0_t* payload, state_id_t* state)
{
    printf("frac1:DIGIT_0\n");
    return 0;
}

static int frac1_DIGIT_1_9_action(const DIGIT_1_9_t* payload, state_id_t* state)
{
    printf("frac1:DIGIT_1_9\n");
    return 0;
}

static int frac1_POINT_action(const POINT_t* payload, state_id_t* state)
{
    printf("frac1:POINT\n");
    return 0;
}

/* opEntered state */
static int opEntered_subtract_op_action(const subtract_op_t* payload, state_id_t* state)
{
    printf("opEntered:subtract_op\n");
    *state = negated2_state;
    
    return 0;
}

static int opEntered_DIGIT_0_action(const DIGIT_0_t* payload, state_id_t* state)
{
    printf("opEntered:DIGIT_0\n");
    *state = zero2_state;
    
    return 0;
}

static int opEntered_DIGIT_1_9_action(const DIGIT_1_9_t* payload, state_id_t* state)
{
    printf("opEntered:DIGIT_1_9\n");
    *state = int2_state;
    
    return 0;
}

static int opEntered_POINT_action(const POINT_t* payload, state_id_t* state)
{
    printf("opEntered:POINT\n");
    *state = frac2_state;
    
    return 0;
}

/* negated2 state */
static int negated2_CE_action(const CE_t* payload, state_id_t* state)
{
    printf("negated2:CE\n");
    *state = opEntered_state;

    return 0;
}

static int negated2_subtract_op_action(const subtract_op_t* payload, state_id_t* state)
{
    printf("negated2:subtract_op\n");
    
    return 0;
}

static int negated2_DIGIT_0_action(const DIGIT_0_t* payload, state_id_t* state)
{
    printf("negated2:DIGIT_0\n");
    *state = zero2_state;
    
    return 0;
}

static int negated2_DIGIT_1_9_action(const DIGIT_1_9_t* payload, state_id_t* state)
{
    printf("negated2:DIGIT_1_9\n");
    *state = int2_state;
    
    return 0;
}

static int negated2_POINT_action(const POINT_t* payload, state_id_t* state)
{
    printf("negated2:POINT\n");
    *state = frac2_state;
    
    return 0;
}

/* operand2 state */
static int operand2_subtract_op_action(const subtract_op_t* payload, state_id_t* state)
{
    printf("operand2:subtract_op\n");
    *state = opEntered_state;
    
    return 0;
}

static int operand2_add_op_action(const add_op_t* payload, state_id_t* state)
{
    printf("operand2:add_op\n");
    *state = opEntered_state;
    
    return 0;
}

static int operand2_multiply_op_action(const multiply_op_t* payload, state_id_t* state)
{
    printf("operand2:multiply_op\n");
    *state = opEntered_state;
    
    return 0;
}

static int operand2_CE_action(const CE_t* payload, state_id_t* state)
{
    printf("operand2:CE\n");
    *state = opEntered_state;
    
    return 0;
}

static int operand2_divide_op_action(const divide_op_t* payload, state_id_t* state)
{
    printf("operand2:divide_op\n");
    *state = opEntered_state;
    
    return 0;
}

static int operand2_EQUALS_action(const EQUALS_t* payload, state_id_t* state)
{
    printf("operand2:EQUALS\n");
    *state = result_state;

    return 0;
}

/* zero2 state */
static int zero2_DIGIT_0_action(const DIGIT_0_t* payload, state_id_t* state)
{
    printf("zero2:DIGIT_0\n");
    return 0;
}

static int zero2_DIGIT_1_9_action(const DIGIT_1_9_t* payload, state_id_t* state)
{
    printf("zero2:DIGIT_1_9\n");
    *state = int2_state;
    
    return 0;
}

static int zero2_POINT_action(const POINT_t* payload, state_id_t* state)
{
    printf("zero2:POINT\n");
    *state = frac2_state;
    
    return 0;
}

/* int2 state */
static int int2_DIGIT_0_action(const DIGIT_0_t* payload, state_id_t* state)
{
    printf("int2:DIGIT_0\n");
    return 0;
}

static int int2_DIGIT_1_9_action(const DIGIT_1_9_t* payload, state_id_t* state)
{
    printf("int2:DIGIT_1_9\n");
    return 0;
}

static int int2_POINT_action(const POINT_t* payload, state_id_t* state)
{
    printf("int2:POINT\n");
    *state = frac2_state;
    
    return 0;
}

/* frac2 state */
static int frac2_DIGIT_0_action(const DIGIT_0_t* payload, state_id_t* state)
{
    printf("frac2:DIGIT_0\n");
    return 0;
}

static int frac2_DIGIT_1_9_action(const DIGIT_1_9_t* payload, state_id_t* state)
{
    printf("frac2:DIGIT_1_9\n");
    return 0;
}

static int frac2_POINT_action(const POINT_t* payload, state_id_t* state)
{
    printf("frac2:POINT\n");
    return 0;
}

/* Merged ignored */
static int ignore_C_action(const C_t* payload, state_id_t* state)
{
    return 0;
}

static int ignore_CE_action(const CE_t* payload, state_id_t* state)
{
    return 0;
}

static int ignore_subtract_op_action(const subtract_op_t* payload, state_id_t* state)
{
    return 0;
}

static int ignore_add_op_action(const add_op_t* payload, state_id_t* state)
{
    return 0;
}

static int ignore_multiply_op_action(const multiply_op_t* payload, state_id_t* state)
{
    return 0;
}

static int ignore_divide_op_action(const divide_op_t* payload, state_id_t* state)
{
    return 0;
}

static int ignore_EQUALS_action(const EQUALS_t* payload, state_id_t* state)
{
    return 0;
}

static int ignore_DIGIT_0_action(const DIGIT_0_t* payload, state_id_t* state)
{
    return 0;
}

static int ignore_DIGIT_1_9_action(const DIGIT_1_9_t* payload, state_id_t* state)
{
    return 0;
}

static int ignore_POINT_action(const POINT_t* payload, state_id_t* state)
{
    return 0;
}

/* external interface */
extern void on_subtract(void)
{
    subtract_op_t payload;
    payload.__dummy__ = 0;

    post_subtract_op_event(&payload);
}

extern void on_add(void)
{
    add_op_t payload;
    payload.__dummy__ = 0;

    post_add_op_event(&payload);
}

extern void on_multiply(void)
{
    multiply_op_t payload;
    payload.__dummy__ = 0;

    post_multiply_op_event(&payload);
}

extern void on_divide(void)
{
    divide_op_t payload;
    payload.__dummy__ = 0;

    post_divide_op_event(&payload);
}

extern void on_clear_entry(void)
{
    CE_t payload;
    payload.__dummy__ = 0;

    post_CE_event(&payload);
}

extern void on_zero(void)
{
    DIGIT_0_t payload;
    payload.__dummy__ = 0;

    post_DIGIT_0_event(&payload);
}

extern void on_digit_1_9(uint8_t digit)
{
    DIGIT_1_9_t payload;
    payload.digit = digit;

    post_DIGIT_1_9_event(&payload);
}

extern void on_point(void)
{
    POINT_t payload;
    payload.__dummy__ = 0;

    post_POINT_event(&payload);
}

extern void on_equals(void)
{
    EQUALS_t payload;
    payload.__dummy__ = 0;

    post_EQUALS_event(&payload);
}

extern void on_clear_all(void)
{
    C_t payload;
    payload.__dummy__ = 0;

    post_C_event(&payload);
}
